/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "ConstantBufferGroup.h"
#include "ConstantBuffer.h"
#include "tinyxml.h"
#include "../EnumConstDeclarations/Shaders.h"
#include <D3DX11.h>
#include "../Messages/ConstantBufferModifier.h"
#include "../Managers/DX11Manager.h"

namespace Hogshead
{
	namespace DX11Rendering
	{
		ConstantBufferGroup::ConstantBufferGroup(void) : _constant_buffers(	14, 2 )
		{

		}


		ConstantBufferGroup::~ConstantBufferGroup(void)
		{
		}

		void ConstantBufferGroup::handleConstantBufferModifier(const ConstantBufferModifier* in_cbm)
		{
			//first check try to find the ConstantBuffer at the requested register
			for( int i = 0; i < size(); i++ )
			{
				ConstantBuffer& cbuffer = _constant_buffers[i];
				if( cbuffer.registerNumber() == in_cbm->_register)
				{
					//copy the data over if it was found, assumes( as it should )
					//that there is only one buffer assigned to a register
					copyFromModifier( cbuffer, in_cbm );
					//found what we were looking for so 'short circuit
					break;
				}				
			}			
		}

		void ConstantBufferGroup::copyFromModifier(ConstantBuffer& destination, const ConstantBufferModifier* source)
		{
			
			//iterate through all the pieces of data that need to be updated
			for( int j = 0; j < source->_data.size(); j++ )
			{
				//check the type to set the internal correctly
				switch (source->_types[j])
				{
				case HFLOAT:
					destination.setInternal<float>(source->_names[j], *(float*)source->_data[j]);
					break;

				case HVECTOR2:
					destination.setInternal<Vector2>(source->_names[j], *(Vector2*)source->_data[j]);
					break;

				case HVECTOR3:
					destination.setInternal<Vector3>(source->_names[j], *(Vector3*)source->_data[j]);
					break;

				case HVECTOR4:
					destination.setInternal<Vector4>(source->_names[j], *(Vector4*)source->_data[j]);
					break;

				case HMATRIX4:
					destination.setInternal<Matrix4>(source->_names[j], *(Matrix4*)source->_data[j]);
					break;

				case HUINT:
					destination.setInternal<unsigned int>(source->_names[j], *(unsigned int*)source->_data[j]);
					break;

				case HINT:
					destination.setInternal<int>(source->_names[j], *(int*)source->_data[j]);
					break;

				case HCOLOR:
					destination.setInternal<Color>(source->_names[j], *(Color*)source->_data[j]);
					break;
				}
				
			}
		}

		ConstantBufferGroup::ConstantBufferGroup(const ConstantBufferGroup& in_other)
		{
			_constant_buffers = in_other._constant_buffers;
		}

		ConstantBufferGroup& ConstantBufferGroup::operator=(const ConstantBufferGroup& in_other)
		{
			_constant_buffers = in_other._constant_buffers;
			return *this;
		}

		int ConstantBufferGroup::size() const
		{
			return _constant_buffers.size();
		}

		const ConstantBuffer& ConstantBufferGroup::get(int index) const
		{
			return _constant_buffers[index];
		}

		ConstantBuffer& ConstantBufferGroup::get(int index)
		{
			return _constant_buffers[index];
		}	

		void ConstantBufferGroup::clear()
		{
			for( int i = 0; i < _constant_buffers.size(); i++ )
			{
				_constant_buffers[i].clear();
			}
			_constant_buffers.clear();
		}

		void ConstantBufferGroup::readXML(const String& in_xml)
		{
			//read in what buffers we want and how we want them mapped
			//parse the String for what we need
			TiXmlDocument document;
			document.Parse(in_xml);

#pragma region First Pass (builds the memory footprint and sets up the ConstantBuffer info)

			TiXmlElement* root_element = document.RootElement();
			String root_tag(root_element->Value());

			if(root_tag.equals("ConstantBufferGroup"))
			{
				TiXmlNode* child_node = NULL;
				TiXmlElement* child_element;
				while(child_node = root_element->IterateChildren(child_node))
				{
					child_element = child_node->ToElement();
					if(!child_element)
						continue;
					
					String tag(child_element->Value());
					if(tag.equals("ConstantBuffer"))
					{
						//now we have a constant buffer we need to add a default (empty) one to our Vector<ConstantBuffer>.
						//The default constructer does minimal work
						int vector_index = _constant_buffers.size();
						_constant_buffers.add ( ConstantBuffer() );

						//read off register and shader bind flags attributes
						ShaderBindFlags shader_flags;
						int register_number;

						child_element->QueryIntAttribute("register", &register_number);

						String shader_bind_flags(child_element->Attribute("shader_bind_flags"));
						//one stage
						if(shader_bind_flags.equals("V"))
						{
							shader_flags = VERTEX_SHADER;
						}
						else if(shader_bind_flags.equals("G"))
						{
							shader_flags = GEOMETRY_SHADER;
						}
						else if(shader_bind_flags.equals("P"))
						{
							shader_flags = PIXEL_SHADER;
						}
						else if(shader_bind_flags.equals("H"))
						{
							shader_flags = HULL_SHADER;
						}
						else if(shader_bind_flags.equals("D"))
						{
							shader_flags = DOMAIN_SHADER;
						}

						//two stages
						else if(shader_bind_flags.equals("VH") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER);
						}
						else if(shader_bind_flags.equals("VD") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | DOMAIN_SHADER);
						}
						else if(shader_bind_flags.equals("VG") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("VP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("HD") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | DOMAIN_SHADER);
						}
						else if(shader_bind_flags.equals("HG") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("HP") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("DG") )
						{
							shader_flags = (ShaderBindFlags)(DOMAIN_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("DP") )
						{
							shader_flags = (ShaderBindFlags)(DOMAIN_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("GP") )
						{
							shader_flags = (ShaderBindFlags)(GEOMETRY_SHADER | PIXEL_SHADER);
						}

						//three stages
						else if(shader_bind_flags.equals("VHD") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | DOMAIN_SHADER);
						}
						else if(shader_bind_flags.equals("VHG") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("VHP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("VGP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("VDG") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("VDP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | DOMAIN_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("VGP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("HDG") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("HDP") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | DOMAIN_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("HGP") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("DGP") )
						{
							shader_flags = (ShaderBindFlags)(DOMAIN_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}

						//four stages
						else if(shader_bind_flags.equals("VHDG") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER);
						}
						else if(shader_bind_flags.equals("VHDP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | DOMAIN_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("VHGP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("HDGP") )
						{
							shader_flags = (ShaderBindFlags)(HULL_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						else if(shader_bind_flags.equals("VDGP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						//five stages
						else if(shader_bind_flags.equals("VHDGP") )
						{
							shader_flags = (ShaderBindFlags)(VERTEX_SHADER | HULL_SHADER | DOMAIN_SHADER | GEOMETRY_SHADER | PIXEL_SHADER);
						}
						//error conditions
						else
						{
							error("bad shader flags given");
						}

						//iterate children to makeup the object
						TiXmlNode* child_child_node = NULL;
						TiXmlElement* child_child_element = NULL;

						while(child_child_node = child_element->IterateChildren(child_child_node))
						{
							//get the size based on types
							child_child_element = child_child_node->ToElement();
							String tag_2(child_child_element->Value());
							String name(child_child_element->Attribute("name"));

							int array_size;
							int result = child_child_element->QueryIntAttribute("arraySize", &array_size);
							if(result == TIXML_NO_ATTRIBUTE)
							{
								array_size = 1;
							}

							// add it to the total
							//save of the displacement as a hash based on string name
							if(tag_2.equals("float"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(float) * array_size );
							}
							else if(tag_2.equals("float2"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(float) * 2  * array_size );
							}
							else if(tag_2.equals("float3"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(float) * 3 * array_size );
							}
							else if(tag_2.equals("float4"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(float) * 4 * array_size );
							}
							else if(tag_2.equals("Matrix4"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(Matrix4) * array_size );
							}
							else if(tag_2.equals("Color"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(Color) * array_size );
							}
							else if(tag_2.equals("UINT"))
							{
								_constant_buffers[vector_index].addPrimitive( name, sizeof(UINT) * array_size );
							}	
							
						}
						//now we need to construct this ConstantBuffer
						_constant_buffers[vector_index].build( register_number, shader_flags );
					}
				}
			}
#pragma endregion

#pragma region Second Pass (reads in primitive data for each ConstantBuffer)

			root_element = document.RootElement();
			root_tag = String(root_element->Value());
			int cbuffer_index = 0;

			if(root_tag.equals("ConstantBufferGroup"))
			{
				TiXmlNode* child_node = NULL;
				TiXmlElement* child_element;

				while(child_node = root_element->IterateChildren(child_node))
				{
					child_element = child_node->ToElement();
					if(!child_element)
						continue;
					
					String tag(child_element->Value());
					if(tag.equals("ConstantBuffer"))
					{
						//iterate children to makeup the object
						TiXmlNode* child_child_node = NULL;
						TiXmlElement* child_child_element = NULL;

						while(child_child_node = child_element->IterateChildren(child_child_node))
						{
							//get the size based on types
							child_child_element = child_child_node->ToElement();
							String tag_2(child_child_element->Value());
							String name(child_child_element->Attribute("name"));

							// add it to the total
							//save of the displacement as a hash based on string name
							if(tag_2.equals("float"))
							{
								float value;
								child_child_element->QueryFloatAttribute("value", &value);
								_constant_buffers[cbuffer_index].setInternal<float>( name, value );								
							}
							else if(tag_2.equals("float2"))
							{
								Vector2 value;
								child_child_element->QueryVector2Attribute("value", &value);
								_constant_buffers[cbuffer_index].setInternal<Vector2>( name, value );
							}
							else if(tag_2.equals("float3"))
							{
								Vector3 value;
								child_child_element->QueryVector3Attribute("value", &value);
								_constant_buffers[cbuffer_index].setInternal<Vector3>( name, value );	
							}
							else if(tag_2.equals("float4"))
							{
								Vector4 value;
								child_child_element->QueryVector4Attribute("value", &value);
								_constant_buffers[cbuffer_index].setInternal<Vector4>( name, value );	
							}
							else if(tag_2.equals("Matrix4"))
							{
								//error("parse for Matrix4 not written");
							}
							else if(tag_2.equals("Color"))
							{
								float r,g,b,a;
								child_child_element->QueryFloatAttribute("r", &r);
								child_child_element->QueryFloatAttribute("g", &g);
								child_child_element->QueryFloatAttribute("b", &b);
								child_child_element->QueryFloatAttribute("a", &a);

								Color value(r,g,b,a);
								_constant_buffers[cbuffer_index].setInternal<Color>( name, value );
							}
													
						}

						//increment where we are in our Vector<ConstantBuffer>.
						cbuffer_index++;
					}
				}
			}
#pragma endregion
		}
	}
}